package it.antonio.ilpizze.ioc;

import java.util.List;

import it.antonio.ilpizze.aop.Aspect;
import it.antonio.ilpizze.aop.Proxifier;
import it.antonio.ilpizze.reflection.Clazz;
import it.antonio.ilpizze.reflection.ConstructorAccessor;

public abstract class ConstructionFactory<T> {
	public abstract Construction<T> construction(Object key, Class<?> type);
	
	public static <T> ConstructionFactory<T> fromConstruction(final Construction<T> cont) {
		return new ConstructionFactory<T>() {
			
			@Override
			public Construction<T> construction(Object key, Class<?> type) {
				return cont;
			}
		};
	}
	
	public static <T> ConstructionFactory<T> emptyConstructor(){
		return new ConstructionFactory<T>() {

			@Override
			public Construction<T> construction(Object key, Class<?> type) {
				return new Construction<T>() {

					@Override
					public T construct(Class<? extends T> type, Dependencies dependencies) {
						return Clazz.get(type).newInstance();
					}
					
					@Override
					public String toString() {
						return "Construction['emptyConstructor']";
					}
				};
				
			}
			
		};
	}
	
	
	@SuppressWarnings("unchecked")
	public static <T> ConstructionFactory<T> autowire(){
		return new ConstructionFactory<T>() {

			@Override
			public Construction<T> construction(Object key, Class<?> type) {
				List<ConstructorAccessor> constructors = Clazz.get(type).findConstructors();
				if(constructors.isEmpty()){
					return (Construction<T>) emptyConstructor().construction(key, type);
				} else {
					final ConstructorAccessor constructor = constructors.get(0);
					return new Construction<T>() {

						@Override
						public T construct(Class<? extends T> type, Dependencies dependencies) {
							Class<?>[] parameterKeys = constructor.getParameterTypes();
							Object[] parameters = new Object[parameterKeys.length];
							for(int i = 0; i < parameterKeys.length; i++){
								parameters[i] = dependencies.get(parameterKeys[i]);
							}
							return (T) constructor.newInstance(parameters);	
						}
						
						@Override
						public String toString() {
							return "Construction['autowire']";
						}
					};
					
					
				}
				
				
			}
			
			
		};
	}
	
	public static <T> ConstructionFactory<T> autowireProxy(final Aspect[] aspects) {
		return new ConstructionFactory<T>() {

			@Override
			public Construction<T> construction(Object key, Class<?> type) {
				List<ConstructorAccessor> constructors = Clazz.get(type).findConstructors();
				if(constructors.isEmpty()){
					return new Construction<T>() {

						@Override
						public T construct(Class<? extends T> type, Dependencies dependencies) {
							return Proxifier.withAspects(aspects).createInstance(type);
						}
						
						@Override
						public String toString() {
							return "Construction['autowireProxy']";
						}
					};
				} else {
					final ConstructorAccessor constructor = constructors.get(0);
					return new Construction<T>() {

						@Override
						public T construct(Class<? extends T> type, Dependencies dependencies) {
							Class<?>[] parameterKeys = constructor.getParameterTypes();
							Object[] parameters = new Object[parameterKeys.length];
							for(int i = 0; i < parameterKeys.length; i++){
								parameters[i] = dependencies.get(parameterKeys[i]);
							}
							return Proxifier.withAspects(aspects).createInstance(type,parameters);
						}
						
						@Override
						public String toString() {
							return "Construction['autowireProxy']";
						}
					};
					
					
				}
			}
			
		};
	}

	
	
	
}
